{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "gpuType": "T4",
      "authorship_tag": "ABX9TyMnrbhuz6pKcD3KsL2y1GDa",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/qunash/r1-overthinker/blob/master/r1_overthinker.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# **🤔 DeepSeek R1 Overthinker**\n",
        "Using this app you can force DeepSeek R1 models to think more deeply, for as long as you wish. It works like this:\n",
        "- Detects when the model tries to conclude thoughts too early (`</think>` token)\n",
        "- Replaces those with prompts that encourage additional reasoning\n",
        "- Continues until a minimum threshold of thinking is reached\n",
        "\n",
        "You decide how long the model should think. The result is more thorough and well-reasoned responses (hopefully).\n",
        "\n",
        "<br>\n",
        "\n",
        "Colab by [anzorq](https://twitter.com/hahahahohohe). If you like it, please consider supporting me:\n",
        "\n",
        "[<a href=\"https://www.buymeacoffee.com/anzorq\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" height=\"32px\" width=\"108px\" alt=\"Buy Me A Coffee\"></a>](https://www.buymeacoffee.com/anzorq)\n",
        "<br>\n",
        "[![GitHub Repo stars](https://img.shields.io/github/stars/qunash/r1-overthinker?style=social)](https://github.com/qunash/r1-overthinker)"
      ],
      "metadata": {
        "id": "nh9kfXMcBf5F"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Install dependencies"
      ],
      "metadata": {
        "id": "VuwKriOTCeXT"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "dPOQkZR9BXiN"
      },
      "outputs": [],
      "source": [
        "%%capture\n",
        "!pip install uv\n",
        "!uv pip install --system unsloth gradio gradio_log\n",
        "!uv pip install --system --force-reinstall --no-deps git+https://github.com/unslothai/unsloth.git\n",
        "\n",
        "#@markdown ### ⬅️ Run this cell"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Run the app"
      ],
      "metadata": {
        "id": "9gzBSCRKCqV9"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#@markdown ### ⬅️ Run this cell\n",
        "#@markdown #### To open the app in a separate tab, click on the generated public URL that looks like this `https://*.gradio.live`\n",
        "import torch\n",
        "from huggingface_hub import HfApi\n",
        "import gc\n",
        "import sys\n",
        "import os\n",
        "\n",
        "class ModelManager:\n",
        "    def __init__(self):\n",
        "        self.current_model_name = None\n",
        "        self.model = None\n",
        "        self.tokenizer = None\n",
        "        self.max_seq_length = 21848\n",
        "        self.dtype = None  # Auto-detects bfloat16/float16\n",
        "        self.load_in_4bit = True\n",
        "        self.log_file = \"loading.log\"\n",
        "\n",
        "        # Clear log file on initialization\n",
        "        open(self.log_file, 'w').close()\n",
        "\n",
        "    class OutputTee:\n",
        "        def __init__(self, *outputs):\n",
        "            self.outputs = outputs\n",
        "        def write(self, data):\n",
        "            for o in self.outputs:\n",
        "                o.write(data)\n",
        "                o.flush()\n",
        "        def flush(self):\n",
        "            for o in self.outputs:\n",
        "                o.flush()\n",
        "\n",
        "    def log_print(self, text):\n",
        "        with open(self.log_file, 'a', encoding='utf-8') as f:\n",
        "            f.write(text + \"\\n\")\n",
        "        print(text)\n",
        "\n",
        "    def get_available_models(self):\n",
        "        api = HfApi()\n",
        "        models = api.list_models(\n",
        "            search=\"DeepSeek-R1\",\n",
        "            author=\"unsloth\",\n",
        "            sort=\"downloads\",\n",
        "            direction=-1\n",
        "        )\n",
        "        model_options = sorted([model.id for model in models])\n",
        "        if not model_options:\n",
        "            self.log_print(\"No DeepSeek-R1 models found from unsloth\")\n",
        "            return []\n",
        "        return model_options\n",
        "\n",
        "    def unload_current_model(self):\n",
        "        if self.model is not None:\n",
        "            try:\n",
        "                self.model.cpu()\n",
        "                del self.model\n",
        "                del self.tokenizer\n",
        "                self.model = None\n",
        "                self.tokenizer = None\n",
        "                self.current_model_name = None\n",
        "                gc.collect()\n",
        "                if torch.cuda.is_available():\n",
        "                    torch.cuda.empty_cache()\n",
        "                    torch.cuda.synchronize()\n",
        "            except Exception as e:\n",
        "                self.log_print(f\"Error during model unloading: {str(e)}\")\n",
        "                if torch.cuda.is_available():\n",
        "                    torch.cuda.empty_cache()\n",
        "\n",
        "    def load_model(self, model_name):\n",
        "        if model_name == self.current_model_name:\n",
        "            yield \"Model already loaded\", False\n",
        "            return\n",
        "\n",
        "        # Clear the log file before starting new load\n",
        "        open(self.log_file, 'w').close()\n",
        "\n",
        "        old_stdout = sys.stdout\n",
        "        old_stderr = sys.stderr\n",
        "        log_fd = open(self.log_file, 'a', encoding='utf-8')\n",
        "        sys.stdout = ModelManager.OutputTee(old_stdout, log_fd)\n",
        "        sys.stderr = ModelManager.OutputTee(old_stderr, log_fd)\n",
        "\n",
        "        try:\n",
        "            self.log_print(f\"Loading model: {model_name}\")\n",
        "            self.unload_current_model()\n",
        "\n",
        "            from unsloth import FastLanguageModel\n",
        "            self.model, self.tokenizer = FastLanguageModel.from_pretrained(\n",
        "                model_name=model_name,\n",
        "                max_seq_length=self.max_seq_length,\n",
        "                dtype=self.dtype,\n",
        "                load_in_4bit=self.load_in_4bit\n",
        "            )\n",
        "            FastLanguageModel.for_inference(self.model)\n",
        "\n",
        "            self.current_model_name = model_name\n",
        "            self.log_print(f\"Successfully loaded {model_name}\")\n",
        "            yield f\"Successfully loaded {model_name}\", True\n",
        "\n",
        "        except Exception as e:\n",
        "            self.log_print(f\"Error loading model: {str(e)}\")\n",
        "            yield f\"Error loading model. See logs for details.\", False\n",
        "        finally:\n",
        "            sys.stdout = old_stdout\n",
        "            sys.stderr = old_stderr\n",
        "            log_fd.close()\n",
        "\n",
        "    def get_current_model_info(self):\n",
        "        return \"No model currently loaded\" if self.current_model_name is None else f\"Currently loaded model: {self.current_model_name}\"\n",
        "\n",
        "    def force_gpu_cleanup(self):\n",
        "        self.unload_current_model()\n",
        "        if torch.cuda.is_available():\n",
        "            initial = torch.cuda.memory_allocated()\n",
        "            gc.collect()\n",
        "            torch.cuda.empty_cache()\n",
        "            final = torch.cuda.memory_allocated()\n",
        "            self.log_print(f\"GPU Memory freed: {(initial - final) / 1024**2:.2f} MB\")\n",
        "\n",
        "    def set_context_length(self, length):\n",
        "        \"\"\"Set the max sequence length\"\"\"\n",
        "        try:\n",
        "            length = int(length)\n",
        "            if length < 1024:\n",
        "                length = 1024\n",
        "            self.max_seq_length = length\n",
        "            return True\n",
        "        except:\n",
        "            return False\n",
        "\n",
        "\n",
        "import gradio as gr\n",
        "import torch\n",
        "import random\n",
        "from gradio import ChatMessage\n",
        "from gradio_log import Log\n",
        "import logging\n",
        "\n",
        "# Initialize model manager\n",
        "model_manager = ModelManager()\n",
        "\n",
        "# Store parameters in global variables since they're just configuration\n",
        "params = {\n",
        "    \"min_thinking_tokens\": 1024,\n",
        "    \"max_output_tokens\": 2048,\n",
        "    \"temperature\": 0.7,\n",
        "    \"top_p\": 0.95,\n",
        "    \"repetition_penalty\": 1.2,\n",
        "    \"max_swaps\": -1,\n",
        "    \"replacement_tokens\": \"\"\"Hold on - what if we challenge our initial assumptions?\n",
        "Let me try a completely different approach to this problem\n",
        "What would be a strong counter-argument to my current reasoning?\n",
        "Let's validate each step of my previous logic\n",
        "Could there be edge cases we haven't considered?\n",
        "What if we work backwards from the expected result?\n",
        "Are there any hidden constraints we're missing?\n",
        "Let's try to disprove our current answer\n",
        "Let me break this down into smaller sub-problems\n",
        "Is there a more elegant solution we're overlooking?\n",
        "What patterns emerge if we look at extreme cases?\n",
        "Could we solve this using an entirely different domain?\"\"\"\n",
        "}\n",
        "\n",
        "def load_selected_model(model_name, context_length):\n",
        "    \"\"\"Wrapper function to handle model loading with progress updates\"\"\"\n",
        "    # First yield should disable the button and inputs\n",
        "    yield [\n",
        "        \"Loading model...\",                   # model_info\n",
        "        0,                                    # current_tab\n",
        "        gr.update(interactive=False),         # load_button\n",
        "        gr.update(interactive=False),         # model_dropdown\n",
        "        gr.update(interactive=False)          # context_length\n",
        "    ]\n",
        "\n",
        "    # Set context length before loading\n",
        "    model_manager.set_context_length(context_length)\n",
        "\n",
        "    for message, success in model_manager.load_model(model_name):\n",
        "        # Return list of values in same order as output components\n",
        "        yield [\n",
        "            message,                          # model_info\n",
        "            1 if success else 0,              # current_tab\n",
        "            gr.update(interactive=True),      # load_button\n",
        "            gr.update(interactive=True),      # model_dropdown\n",
        "            gr.update(interactive=True)       # context_length\n",
        "        ]\n",
        "\n",
        "def get_model_info():\n",
        "    return model_manager.get_current_model_info()\n",
        "\n",
        "def generate_with_replacements(message, history):\n",
        "    if model_manager.model is None:\n",
        "        return [ChatMessage(role=\"assistant\", content=\"Please load a model first in the 'Choose Model' tab\")]\n",
        "\n",
        "    # Convert history to messages format\n",
        "    messages = [{\"role\": msg[\"role\"], \"content\": msg[\"content\"]} for msg in history]\n",
        "    # Add current message\n",
        "    messages.append({\"role\": \"user\", \"content\": message})\n",
        "\n",
        "    # Initialize tokens with full conversation history\n",
        "    current_tokens = model_manager.tokenizer.apply_chat_template(\n",
        "        messages,\n",
        "        tokenize=True,\n",
        "        add_generation_prompt=True,\n",
        "        return_tensors=\"pt\"\n",
        "    ).to(\"cuda\")\n",
        "\n",
        "    # Find think tags using current tokenizer\n",
        "    _, start_think_token, end_think_token = model_manager.tokenizer.encode(\"<think></think>\")\n",
        "\n",
        "    thinking_msg = None\n",
        "    final_msg = None\n",
        "    thinking_content = \"\"\n",
        "    final_content = \"\"\n",
        "    n_thinking_tokens = 0\n",
        "    swap_count = 0\n",
        "    is_thinking = False\n",
        "    total_new_tokens = 0\n",
        "    max_batch_tokens = 20\n",
        "\n",
        "    while total_new_tokens < params[\"max_output_tokens\"]:\n",
        "        outputs = model_manager.model.generate(\n",
        "            input_ids=current_tokens,\n",
        "            max_new_tokens=min(max_batch_tokens, params[\"max_output_tokens\"] - total_new_tokens),\n",
        "            temperature=params[\"temperature\"],\n",
        "            top_p=params[\"top_p\"],\n",
        "            repetition_penalty=params[\"repetition_penalty\"],\n",
        "            use_cache=True,\n",
        "            return_dict_in_generate=True,\n",
        "            output_scores=True\n",
        "        )\n",
        "\n",
        "        new_tokens = outputs.sequences[:, current_tokens.shape[1]:]\n",
        "        total_new_tokens += new_tokens.shape[1]\n",
        "\n",
        "        for i, token in enumerate(new_tokens[0]):\n",
        "            token_id = token.item()\n",
        "\n",
        "            # Check for token swapping before processing the token\n",
        "            if (token_id in (end_think_token, model_manager.model.config.eos_token_id)\n",
        "                and n_thinking_tokens < params[\"min_thinking_tokens\"]\n",
        "                and (params[\"max_swaps\"] == -1 or swap_count < params[\"max_swaps\"])):\n",
        "\n",
        "                replacement = \"\\n\" + random.choice([t.strip() for t in params[\"replacement_tokens\"].split('\\n') if t.strip()])\n",
        "                replacement_tokens = model_manager.tokenizer.encode(replacement)\n",
        "\n",
        "                n_thinking_tokens += len(replacement_tokens)\n",
        "                swap_count += 1\n",
        "                thinking_content += replacement\n",
        "\n",
        "                current_tokens = torch.cat([\n",
        "                    outputs.sequences[:, :current_tokens.shape[1] + i],\n",
        "                    torch.tensor([replacement_tokens]).to(\"cuda\")\n",
        "                ], dim=1)\n",
        "\n",
        "                thinking_msg.content = thinking_content\n",
        "                thinking_msg.metadata = {\n",
        "                    \"title\": f\"🤔 Thinking process (Extensions: {swap_count}, Tokens: {n_thinking_tokens})\"\n",
        "                }\n",
        "                yield [thinking_msg]\n",
        "                break\n",
        "\n",
        "            token_str = model_manager.tokenizer.decode([token_id])\n",
        "\n",
        "            # Update thinking state\n",
        "            if token_id == start_think_token:\n",
        "                is_thinking = True\n",
        "                thinking_msg = ChatMessage(\n",
        "                    role=\"assistant\",\n",
        "                    content=\"\",\n",
        "                    metadata={\"title\": \"🤔 Thinking process\"}\n",
        "                )\n",
        "                continue\n",
        "            elif token_id == end_think_token:\n",
        "                is_thinking = False\n",
        "                final_msg = ChatMessage(role=\"assistant\", content=\"\")\n",
        "                continue\n",
        "\n",
        "            # Add content and update messages\n",
        "            if is_thinking:\n",
        "                thinking_content += token_str\n",
        "                n_thinking_tokens += 1\n",
        "                if thinking_msg:\n",
        "                    thinking_msg.content = thinking_content\n",
        "                    thinking_msg.metadata = {\n",
        "                        \"title\": f\"🤔 Thinking process (Extensions: {swap_count}, Tokens: {n_thinking_tokens})\"\n",
        "                    }\n",
        "                    yield [thinking_msg]\n",
        "            else:\n",
        "                final_content += token_str\n",
        "                if final_msg:\n",
        "                    messages = []\n",
        "                    if thinking_msg:\n",
        "                        messages.append(thinking_msg)\n",
        "                    final_msg.content = final_content.strip()\n",
        "                    messages.append(final_msg)\n",
        "                    yield messages\n",
        "\n",
        "            if token_id == model_manager.model.config.eos_token_id:\n",
        "                messages = []\n",
        "                if thinking_msg:\n",
        "                    thinking_msg.content = thinking_content\n",
        "                    messages.append(thinking_msg)\n",
        "                if final_msg:\n",
        "                    final_msg.content = final_content.strip()\n",
        "                    messages.append(final_msg)\n",
        "                yield messages\n",
        "                return\n",
        "\n",
        "        else:\n",
        "            current_tokens = outputs.sequences\n",
        "\n",
        "def update_global_params(min_tokens, max_tokens, max_swaps, replacements, temp, top_p, rep_pen):\n",
        "    params.update({\n",
        "        \"min_thinking_tokens\": min_tokens,\n",
        "        \"max_output_tokens\": max_tokens,\n",
        "        \"max_swaps\": max_swaps,\n",
        "        \"replacement_tokens\": replacements,\n",
        "        \"temperature\": temp,\n",
        "        \"top_p\": top_p,\n",
        "        \"repetition_penalty\": rep_pen\n",
        "    })\n",
        "\n",
        "# Create the interface\n",
        "with gr.Blocks() as demo:\n",
        "    gr.Markdown(\"\"\"\n",
        "        # 🤔 DeepSeek R1 Overthinker\n",
        "\n",
        "        Using this app you can force DeepSeek R1 models to think more deeply, for as long as you wish. It works like this:\n",
        "        - Detects when the model tries to conclude thoughts too early (`</think>` token)\n",
        "        - Replaces those with prompts that encourage additional reasoning\n",
        "        - Continues until a minimum threshold of thinking is reached\n",
        "\n",
        "        You decide how long the model should think. The result is more thorough and well-reasoned responses (hopefully).\n",
        "    \"\"\")\n",
        "\n",
        "    current_tab = gr.State(value=0)\n",
        "\n",
        "    with gr.Tabs() as tabs:\n",
        "        with gr.Tab(\"1. Choose Model\"):\n",
        "            model_dropdown = gr.Dropdown(\n",
        "                choices=model_manager.get_available_models(),\n",
        "                label=\"Select DeepSeek R1 Model\",\n",
        "                interactive=True\n",
        "            )\n",
        "\n",
        "            context_length = gr.Number(\n",
        "                value=21848,\n",
        "                label=\"Maximum context length (in tokens)\",\n",
        "                precision=0,\n",
        "                minimum=1024,\n",
        "                info=\"\"\"Higher values require more VRAM. Reference values from Llama 3.1 testing:\n",
        "3,000 tokens → ~8 GB VRAM\n",
        "22,000 tokens → ~12 GB VRAM\n",
        "41,000 tokens → ~16 GB VRAM\n",
        "78,000 tokens → ~24 GB VRAM\n",
        "154,000 tokens → ~40 GB VRAM\n",
        "\n",
        "Actual VRAM usage depends on the model. Start with a lower value if you experience out-of-memory errors.\"\"\"\n",
        "            )\n",
        "\n",
        "            load_button = gr.Button(\"Load Selected Model\")\n",
        "            model_info = gr.Markdown(get_model_info())\n",
        "\n",
        "            # Create log component with auto-refresh\n",
        "            loading_log = Log(\n",
        "                model_manager.log_file,\n",
        "                dark=True,\n",
        "                xterm_font_size=14,\n",
        "                label=\"Loading Progress\",\n",
        "                every=0.5\n",
        "            )\n",
        "\n",
        "            # Update model info when loading model\n",
        "            load_button.click(\n",
        "                fn=load_selected_model,\n",
        "                inputs=[model_dropdown, context_length],\n",
        "                outputs=[\n",
        "                    model_info,           # Markdown\n",
        "                    current_tab,          # State\n",
        "                    load_button,          # Button\n",
        "                    model_dropdown,       # Dropdown\n",
        "                    context_length        # Number\n",
        "                ],\n",
        "                queue=True\n",
        "            ).success(                    # Change .then to .success\n",
        "                fn=lambda tab: gr.Tabs(selected=tab),\n",
        "                inputs=[current_tab],\n",
        "                outputs=[tabs]\n",
        "            )\n",
        "\n",
        "        with gr.Tab(\"2. Chat\", id=1):\n",
        "            with gr.Row():\n",
        "                with gr.Column(scale=3):\n",
        "                    chat_interface = gr.ChatInterface(\n",
        "                        generate_with_replacements,\n",
        "                        chatbot=gr.Chatbot(\n",
        "                            bubble_full_width=True,\n",
        "                            show_copy_button=True,\n",
        "                            latex_delimiters=[\n",
        "                                {\"left\": \"$$\", \"right\": \"$$\", \"display\": True},\n",
        "                                {\"left\": \"$\", \"right\": \"$\", \"display\": False},\n",
        "                                {\"left\": \"\\\\(\", \"right\": \"\\\\)\", \"display\": False},\n",
        "                                {\"left\": \"\\\\[\", \"right\": \"\\\\]\", \"display\": True},\n",
        "                                {\"left\": \"\\\\begin{equation}\", \"right\": \"\\\\end{equation}\", \"display\": True},\n",
        "                                {\"left\": \"\\\\begin{align}\", \"right\": \"\\\\end{align}\", \"display\": True},\n",
        "                                {\"left\": \"\\\\begin{alignat}\", \"right\": \"\\\\end{alignat}\", \"display\": True},\n",
        "                                {\"left\": \"\\\\begin{gather}\", \"right\": \"\\\\end{gather}\", \"display\": True},\n",
        "                                {\"left\": \"\\\\begin{CD}\", \"right\": \"\\\\end{CD}\", \"display\": True},\n",
        "                                {\"left\": \"\\\\boxed{\", \"right\": \"}\", \"display\": False},\n",
        "                                {\"left\": \"\\\\frac{\", \"right\": \"}\", \"display\": False},\n",
        "                                {\"left\": \"\\\\sqrt{\", \"right\": \"}\", \"display\": False}\n",
        "                            ],\n",
        "                            type=\"messages\",\n",
        "                            editable='all',\n",
        "                            min_height=550,\n",
        "                        ),\n",
        "                        fill_height=True,\n",
        "                        type=\"messages\"\n",
        "                    )\n",
        "\n",
        "                with gr.Column(scale=1):\n",
        "                    min_tokens = gr.Slider(minimum=0, maximum=model_manager.max_seq_length-512, value=1024, step=128, label=\"Minimum Thinking Tokens\")\n",
        "                    max_tokens = gr.Slider(minimum=256, maximum=model_manager.max_seq_length, value=2048, step=256, label=\"Maximum Output Tokens\")\n",
        "                    max_swaps = gr.Slider(minimum=-1, maximum=20, value=-1, step=1, label=\"Maximum Reasoning Extensions\", info=\"Limit how many times to extend the model's reasoning (-1 for unlimited).\")\n",
        "                    replacements = gr.Textbox(value=params[\"replacement_tokens\"], label=\"Replacement Tokens (one per line)\", lines=5, max_lines=5)\n",
        "                    temperature = gr.Slider(minimum=0.1, maximum=2.0, value=0.7, step=0.1, label=\"Temperature\")\n",
        "                    top_p = gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label=\"Top-p\")\n",
        "                    rep_penalty = gr.Slider(minimum=1.0, maximum=2.0, value=1.2, step=0.1, label=\"Repetition Penalty\")\n",
        "\n",
        "                    # Connect all parameter components to the update function\n",
        "                    for param in [min_tokens, max_tokens, max_swaps, replacements, temperature, top_p, rep_penalty]:\n",
        "                        param.change(\n",
        "                            fn=update_global_params,\n",
        "                            inputs=[min_tokens, max_tokens, max_swaps, replacements, temperature, top_p, rep_penalty],\n",
        "                            outputs=[]\n",
        "                        )\n",
        "    gr.HTML(\"\"\"\n",
        "    <div style=\"border-top: 1px solid #303030;\">\n",
        "      <br>\n",
        "      <p>App by: <a href=\"https://twitter.com/hahahahohohe\"><img src=\"https://img.shields.io/twitter/follow/hahahahohohe?label=%40anzorq&style=social\" alt=\"Twitter Follow\"></a></p><br>\n",
        "      <p>Enjoying this app? Please consider <a href=\"https://www.buymeacoffee.com/anzorq\">supporting me</a></p>\n",
        "      <a href=\"https://www.buymeacoffee.com/anzorq\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" style=\"height: 45px !important;width: 162px !important;\" ></a><br><br>\n",
        "      <a href=\"https://github.com/qunash/r1-overthinker\" target=\"_blank\"><img alt=\"GitHub Repo stars\" src=\"https://img.shields.io/github/stars/qunash/r1-overthinker?style=social\"></a>\n",
        "    </div>\n",
        "    \"\"\")\n",
        "\n",
        "demo.launch(debug=True, share=True)\n"
      ],
      "metadata": {
        "cellView": "form",
        "id": "8tGRgwQuCquC"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}